`include "All.v"

module Assign5(SW,KEY,HEX0,HEX1,HEX2,HEX3,CLOCK_50,LEDR,LEDG); 
	input  [9:0] SW;
	input  [3:0] KEY;
	input  CLOCK_50;
	output [6:0] HEX0,HEX1,HEX2,HEX3;
	output [7:0] LEDG;
	output [9:0] LEDR;

	//wire clk = CLOCK_50;
	//wire lock = 1;
	wire lock,clk;
	Assign5_PLL	Assign5_PLL_inst (.inclk0 ( CLOCK_50 ),.c0 ( clk ),.locked ( lock ));
	
	wire [3:0] keys=KEY;
	wire [9:0] sws=SW;
	wire [9:0] ledred;
	wire [7:0] ledgreen;
	wire [6:0] digit0,digit1,digit2,digit3;
    assign {HEX0,HEX1,HEX2,HEX3,LEDR,LEDG}={digit0,digit1,digit2,digit3,ledred,ledgreen};
	assign LEDR = ledred;
    assign LEDG = ledgreen;
	parameter DBITS=32;
	
	wire [(DBITS-1):0] abus;
	tri  [(DBITS-1):0] dbus;
	wire we;

	reg [3:0] CTL0; //Bit 0 is IE Bit 1 is OIE Bit 3 is CM Bit 4 is OM
	reg [3:0] CTL1; //holds ID of device
	reg [3:0] CTL1old;
	wire [31:0] memaddr_M; // Address bus is driven with the memaddr value
	assign memaddr_M = aluout_M;
	assign abus=memaddr_M;
	assign we=wmem_M; // The write-enable signal is driven with the wmem value
	// The data bus is driven by the processor only when writing
	// (When reading, dbus is driven by memory/device we read from)
	assign dbus=wmem_M?regout2_M:{DBITS{1'bz}};

	Memory #(.BITS(DBITS),.RABITS(13),.MFILE("Assign5.mif"))
			memory(.IADDR(PC_F),.IOUT(inst_F),
			.ABUS(abus),.DBUS(dbus),.WE(we),
			.CLK(clk),.LOCK(lock),.INIT(init));

	wire intr_timer;
	// DIVN is set for 25MHz, it tells the timer how many cycles need to pass
	// before it can decrement TCNT - and those decrements need to happen 1000 times per second
	// If you change the frequency, remember to change the timer's DIVN to still have TCNT
	// be decremented at 1000 times per second
	Timer #(.BITS(DBITS),.BASE(32'hFFFFF100),.DIVN(60000),.DIVB(16))
			timer(.ABUS(abus),.DBUS(dbus),.WE(we),.INTR(intr_timer),.CLK(clk),.LOCK(lock),.INIT(init),.DEBUG());

	Display #(.BITS(DBITS),.BASE(32'hFFFFF800))
			display(.ABUS(abus),.DBUS(dbus),.WE(we),.CLK(clk),.LOCK(lock),.INIT(init),
			.HEX0(digit0),.HEX1(digit1),.HEX2(digit2),.HEX3(digit3));

	Leds #(.ABITS(DBITS),.DBITS(DBITS),.LBITS(10),.BASE(32'hFFFFF804))
		ledsr(.ABUS(abus),.DBUS(dbus),.WE(we),.CLK(clk),.LOCK(lock),.INIT(init),.LED(ledred));

	Leds #(.ABITS(DBITS),.DBITS(DBITS),.LBITS(8),.BASE(32'hFFFFF808))
		ledsg(.ABUS(abus),.DBUS(dbus),.WE(we),.CLK(clk),.LOCK(lock),.INIT(init),.LED(ledgreen));

	wire intr_keys;
	KeyDev #(.BITS(DBITS),.BASE(32'hFFFFF000))
		keyDev(.ABUS(abus),.DBUS(dbus),.WE(we),.INTR(intr_keys),.CLK(clk),.LOCK(lock),.INIT(init),.KEY(keys),.DEBUG());
	wire intr_sws;
	// DEBN tells the SW device how many cycles must pass until a change in raw SW state is considered debounced
	// (At 25MHz, there are 250,000 cycles in 10ms; if you change the frequency, change DEBN here, too)
	SwDev #(.BITS(DBITS),.BASE(32'hFFFFF004),.DEBN(32'd600000),.DEBB(20))
		swDev(.ABUS(abus),.DBUS(dbus),.WE(we),.INTR(intr_sws),.CLK(clk),.LOCK(lock),.INIT(init),.SW(sws),.DEBUG());
  
	// Illegal instruction signal generated and used in R stage
	// Note how we don't signal an illegal instruction if we're hoing to
	// flush it because a mispredicted branch is in the A stage
	wire iinst_R=iinst_raw&&(!mispred_A);
	// Determine mode using the instruction word and current mode
	// CtlIInst simply looks at the instruction word and decides if it's legal
	// in the current mode (cmode_R, which is tied to CTL0[2])
	wire iinst_raw;
	wire cmode_R=(!isWctl0_A) ? CTL0[2] : regout1_A[2];
	wire isWctl0_R, isRctl0_R, isWctl1_R, isRctl1_R;
	CtlIInst ctlIInst(.IWORD(inst_R),.CMODE(cmode_R),.IINST(iinst_raw));
	sysCallCtl sysCtl(.IWORD(inst_R),.WRITECTL0(isWctl0_R), .READCTL0(isRctl0_R),.WRITECTL1(isWctl1_R), .READCTL1(isRctl1_R));
	
	// We want to have an interrupt or exception if we have an illegal instruction (regardless of IE),
	// or if we have an interrupt request while interrupts are enabled (CTL0[0]) and while we are not already
	// in the process of jumping to the interrupt handler
	wire intr=iinst_R ||(CTL0[0]&&(!inta_A)&&(intr_timer||intr_keys||intr_sws));
	// This is where we determine the number to put in CTL1
	// In my implementation CTLx are read/written in the A stage,
	// so I have an iinst_A signal to indicate that an instruction
	// that was detected as illegal (and was killed) is now in A stage
	wire [3:0] intn=iinst_A   ?4'h0:
					intr_timer?4'h1:
					intr_keys? 4'h2:
					intr_sws?  4'h3:4'hF;
	wire isRETI_R = (inst_R==32'h80cc0000);
	reg  [(DBITS-1):0] PC; // Program Counter (value used in F stage, updated using inputs from F and A stages)
	//not sure which pc so set for predicted
	wire [(DBITS-1):0] PCpred_F = (!brTarget[32:32])? PC4_F : brTarget[31:0];
	wire [(DBITS-1):0] PC_F=PC;
	// PC+4 generated in the F stage (used in F to update PC, also used for branch and JRL insts in later stages)
	wire [(DBITS-1):0] PC4_F=PC_F+32'd4;
	
	reg init=1'b1;
	always @(posedge clk) if(lock) begin
		init<=1'b0;
	end
	
	wire [DBITS:0] brTarget;
	BranchTable #(.DBITS(DBITS+1),.ABITS(8),.MFILE("Assign4BPR.mif"))
			brTable(.RADDR1(PC_F[7:0]),.DOUT1(brTarget),.WADDR(prevPC_A[7:0]),
			.DIN(brTableUpdate),.WE(ctrlflush_A),.CLK(clk),.LOCK(lock));
	
	always @(posedge clk) if(lock) begin
		PC <= (init)        ? 32'h400 :
			  (inta_A)      ? 32'h200 :
			  (ctrlflush_A) ? PCcorr_A:
			  (stall_R)     ? PC_F    :PCpred_F;
		CTL0<=(init) 				? 4'b0100 					  :
			  (isRETI_A) 			? {1'b0,CTL0[3],1'b0,CTL0[1]} :
			  (inta_A || iinst_A) 	? {CTL0[2],1'b1,CTL0[0],1'b0} :
			  (isWctl0_A) 			? regout1_A[3:0] 			  : CTL0;
		CTL1<=(iinst_A)   ? intn           :
			  (inta_A)    ? intn_A         :
			  (isWctl1_A) ? regout1_A[3:0] :CTL1;
		CTL1old<=(iinst_A || inta_A || isWctl1_A)?CTL1:CTL1old;
	end
	
	// Register file, read in R stage, written in W stage **AND ALU stage to support for interupts
	RegFile  #(.DBITS(DBITS),.ABITS(4),.MFILE("Assign4R.mif"))
		regFile(.RADDR1(rregno1_R),.DOUT1(regout1_R),.RADDR2(rregno2_R),.DOUT2(regout2_R),
		.WADDR1(wregno_W),.DIN1(regval_W), .WADDR2(wadd2),.DIN2(r2din),.WE1(wrreg_W),
		.WE2(isRctl0_A||isRctl1_A|| inta_A),.CLK(clk),.LOCK(lock));
	wire [31:0] r2din = (iinst_A)   ? prevPC_A:
						(inta_A)    ? prevPC_A:
						(isRctl0_A) ? CTL0:
						(isRctl1_A) ? CTL1:
									  32'hXXXXXXXX;
	wire [3:0] wadd2 = (inta_A || iinst_A)    ? 4'hC :
					   (isRctl0_A||isRctl1_A) ? wregno_A:
												4'hX;
	wire [(DBITS-1):0] inst_F; // Instruction word, generated (from MemSys, see above) in the FE stage
	wire [3:0] rregno1_F,rregno2_F; // Register numbers for reading generated and used in F stage
	CtlRReg ctlRReg(.IWORD(inst_F),.RREGNO1(rregno1_F),.RREGNO2(rregno2_F));

	// Pipeline registers between F and R stages
	reg [(DBITS-1):0] inst_R=32'b0,PC4_R,PCpred_R, prevPC_R;
	reg [3:0] rregno1_R,rregno2_R;
	always @(posedge clk) if(lock) begin // Get new F->R signals each locked clock cycle
		{prevPC_R, PC4_R, PCpred_R, rregno1_R,rregno2_R,inst_R} <= (init || ctrlflush_A || inta_A) ? {32'b0, 32'b0, 32'b0, 4'b0,4'b0,32'b0}:
																   (stall_R) 					   ? {prevPC_R, PC4_R, PCpred_R, rregno1_R,rregno2_R,inst_R}:
																									 {PC_F, PC4_F, PCpred_F, rregno1_F,rregno2_F,inst_F};
	end

	wire [(DBITS-1):0] regout1_R,regout2_R; // Register outputs generated in R stage (from RegFile, see above), used in subsequent stages
	wire [(DBITS-1):0] immval_R; // Immediate value sign-extension and *4 (if needed) is done in R and used in R (for ALU) and A (for branches)
	CtlImm #(.DBITS(DBITS)) ctlImm(.IWORD(inst_R),.IMMVAL(immval_R));
	// Control for the second ALU input value (reg, imm, or zero) is generated in R, used in R
	// aluin2i decides between register and non-register value for ALU input 2; it is used in A
	// aluin2z selects between sxt(imm) and zero for the non-reg value; it is used in R
	// Note that aluin2z matters only when aluin2i is 1
	wire aluin2z_R,aluin2i_R;
	CtlALUIn ctlALUIn(.IWORD(inst_R),.ALUIN2Z(aluin2z_R),.ALUIN2I(aluin2i_R));
	// If non-reg used as second ALU input value, it is zero or sxt(imm). We select between them in R
	// The result of this selection goes to A stage, where we select between it and regout2
	wire [(DBITS-1):0] aluimm_R=aluin2z_R?32'd0:immval_R;
	// ALU function controls generated in R, used in A stage
	// Logic result enabled if alulog is 1
	// Logic operation selected by aluand,aluor,aluxor
	// Logic result inverted if lognot is 1
	wire alulog_R,logand_R,logor_R,logxor_R,lognot_R;
	// Arithmetic and comparison operations use the adder/subtractor
	// It adds if addsub is 0, subtracts otherwise
	wire addsub_R;
	// Comparison result enabled if alucmp is 1
	// Comparison result is eq, lt, or both (le)
	// Comparison result inverted if cmpnot_g is 1
	wire alucmp_R,cmpeq_R,cmplt_R,cmpnot_R;
	wire aluari_R; // Enables arithmetic result (ALU result comes directly from the adder/subtractor)
	CtlALU ctlALU(.IWORD(inst_R),.ALULOG(alulog_R),.LOGAND(logand_R),.LOGOR(logor_R),
		.LOGXOR(logxor_R),.LOGNOT(lognot_R),.ADDSUB(addsub_R),.ALUCMP(alucmp_R),
		.CMPEQ(cmpeq_R),.CMPLT(cmplt_R),.CMPNOT(cmpnot_R),.ALUARI(aluari_R));
	wire isbranch_R,isjump_R,savepc_R; // Branch/jump control signals generated in R, used in A stage
	BraCtl braCtl(.IWORD(inst_R),.ISBRANCH(isbranch_R),.ISJUMP(isjump_R),.SAVEPC(savepc_R));
	wire wmem_R,rmem_R; // Memory read and write control (generated in R, used in M and W stages)
	CtlWMem ctlWMem(.IWORD(inst_R),.WMEM(wmem_R),.RMEM(rmem_R));
	wire [3:0] wregno_R; // Register write control, generated in R and used in W stages
	wire wrreg_R;
	CtlWReg ctlWReg(.IWORD(inst_R),.WREGNO(wregno_R),.WRREG(wrreg_R));
  
	//DATA HAZARD
	//check to see if there is a data hazard dependency
	wire ddep1A_R=wrreg_A && (rregno1_R==wregno_A);
	wire ddep1M_R=wrreg_M && (rregno1_R==wregno_M);
	wire ddep2A_R=(wmem_R || !aluin2i_R) && (wrreg_A) && (rregno2_R==wregno_A);
	wire ddep2M_R=(wmem_R || !aluin2i_R) && (wrreg_M) && (rregno2_R==wregno_M);
	//LW dependancy, manditory stall  r(I) | a(LW) | Mem | WB
	wire stall_R=(rmem_A && (ddep1A_R||ddep2A_R) );// || (ddep2A_R) || (ddep2M_R);
	//wire ddep3A_M=rmem_A&&(ddep1A_R||ddep2A_R); // && rmem_R;
	//wire stall_R=(ddep1A_R||ddep1M_R||ddep2A_R||ddep2M_R||ddep3A_M);//ddep3M_M);
  
	/* registers from R to A */
	reg [(DBITS-1):0] PC4_A, regout1_A, regout2_A, immval_A, aluimm_A;
	reg aluin2i_A, alulog_A, logand_A, logor_A, logxor_A, lognot_A;
	reg addsub_A, alucmp_A, cmpeq_A, cmplt_A, cmpnot_A;
	reg aluari_A, isbranch_A, isjump_A, savepc_A;
	reg wmem_A, rmem_A, wrreg_A;
	reg [3:0] wregno_A;
	reg isWctl0_A, isRctl0_A, isWctl1_A, isRctl1_A, inta_A;
	reg iinst_A;
	reg isRETI_A;
	reg [3:0] intn_A;
    reg [(DBITS-1):0] PCpred_A, prevPC_A;
    reg stall_A;
	always @(posedge clk) if(lock) begin
		regout1_A <= (!init)&&(ddep1A_R && rmem_A)   ? 32'b0 : 
					 (!init)&&(ddep1A_R && savepc_A) ? PC4_A:
					 (!init)&&(ddep1A_R) 			 ? aluout_A:
					 (!init)&&(ddep1M_R) 			 ? regval_M:
					 (!init) 			 			 ? regout1_R:
													   32'hXXXXXXXX;
		regout2_A <= (!init)&&(ddep2A_R && rmem_A)   ? 32'b0 : 
					 (!init)&&(ddep2A_R && savepc_A) ? PC4_A:
					 (!init)&&(ddep2A_R) 			 ? aluout_A:
					 (!init)&&(ddep2M_R) 			 ? regval_M:
					 (!init) 			 			 ? regout2_R:
													   32'hXXXXXXXX;
		{isbranch_A,isjump_A,wmem_A,wrreg_A, savepc_A, rmem_A,isWctl0_A, isRctl0_A, 
			isWctl1_A, isRctl1_A, isRETI_A, intn_A, inta_A, iinst_A } <= (ctrlflush_A || stall_R || init) ? {17'b0}:
																		 (intr) 						  ? {11'b0,intn, intr, iinst_R }:	
																											{isbranch_R,isjump_R,wmem_R,wrreg_R, savepc_R, rmem_R, isWctl0_R, isRctl0_R,
																												isWctl1_R, isRctl1_R, isRETI_R, intn, intr, iinst_R };
		{immval_A,	aluin2i_A,	wregno_A				} <= (!init) ? {immval_R, aluin2i_R, wregno_R		   } : {32'hXXXXXXXX,1'bX,4'bX};
		{aluimm_A,	alulog_A,	logand_A,	logor_A		} <= (!init) ? {aluimm_R, alulog_R,  logand_R, logor_R } : {1'bX,1'bX, 1'bX,1'bX};
		{logxor_A,	lognot_A,	addsub_A,	alucmp_A	} <= (!init) ? {logxor_R, lognot_R,	 addsub_R, alucmp_R} : {1'bX,1'bX,1'bX,1'bX};
		{cmpeq_A,	cmplt_A,	cmpnot_A,	aluari_A	} <= (!init) ? {cmpeq_R,  cmplt_R,	 cmpnot_R, aluari_R} : {1'bX,1'bX,1'bX,1'bX};
		{PC4_A,PCpred_A}<=((ctrlflush_A || stall_R  || init)) ? {32'b0, 32'b0}: {PC4_R, PCpred_R};
		prevPC_A <= (stall_A || ctrlflush_M) ? PCcorr_M:
											   prevPC_R;
		stall_A <=stall_R;
	end

	wire [(DBITS-1):0] aluin1_A=regout1_A; // ALU input values (generated and used in A stage)
	wire [(DBITS-1):0] aluin2_A=aluin2i_A?aluimm_A:regout2_A;
	// The ALU generates its result in the A stage, it is used in A, M,and W stages
	// Generate logic result (or zero if  select it or ignore it)
	wire [(DBITS-1):0] andout_A={DBITS{logand_A}}&(aluin1_A&aluin2_A);
	wire [(DBITS-1):0] orout_A ={DBITS{logor_A }}&(aluin1_A|aluin2_A);
	wire [(DBITS-1):0] xorout_A={DBITS{logxor_A}}&(aluin1_A^aluin2_A);
	wire [(DBITS-1):0] logtmp_A=(andout_A|orout_A|xorout_A);
	wire [(DBITS-1):0] logres_A=lognot_A?(~logtmp_A):logtmp_A;
	wire [(DBITS-1):0] logout_A=logres_A&{DBITS{alulog_A}};
	wire [(DBITS-1):0] arires_A=aluin1_A+(addsub_A?(~aluin2_A):aluin2_A)+addsub_A; // This is our adder/subtractor (if subtract, invert 2nd input, add 1)
	wire [(DBITS-1):0] ariout_A=arires_A&{DBITS{aluari_A}};
	wire eqout_A=cmpeq_A&&(aluin1_A==aluin2_A); // Generate comparison result (lt uses subtration result, so addsub must be 1 for lt or lte)
	wire ltout_A=cmplt_A&&arires_A[31];
	wire cmptmp_A=eqout_A||ltout_A;
	wire cmpres_A=cmpnot_A?(!cmptmp_A):cmptmp_A;
	wire [(DBITS-1):0] cmpout_A={{(DBITS-1){1'b0}},cmpres_A&&alucmp_A};
	wire [(DBITS-1):0] aluout_A=logout_A|ariout_A|cmpout_A; // Final ALU result
	wire [(DBITS-1):0] PCBT_A = (PC4_A+immval_A); // PCBT_A (target address for a branch, will be used only if this is a taken branch)
	wire [(DBITS-1):0] PCJT_A = aluout_A; //PCJT_A (target address for a jump, will be used only if this is a jump)
	wire usebtarg_A = isbranch_A?aluout_A[0]:1'b0; //usebtarg_A (one-bit signal that is set to 1 if this is a taken branch)
	wire usejtarg_A = isjump_A; //usejtarg_A (one-bit signal that is set to 1 if this is a jump)
	
	//CONTROL HAZARD
	wire [(DBITS-1):0] PCcorr_A=usebtarg_A?PCBT_A: //detect if a branch or jump is going to occur
								usejtarg_A?PCJT_A:
											PC4_A;
	wire [(DBITS):0] brTableUpdate = {{1'b1},{PCcorr_A}};	
	wire ctrlflush_A=(PCpred_A!=PCcorr_A);
	wire mispred_A=ctrlflush_A;
	
	//registers from A to M
	reg [(DBITS-1):0] PC4_M, aluout_M, regout2_M, PCcorr_M;
	reg savepc_M, wrreg_M, wmem_M, rmem_M;
	reg [3:0] wregno_M;
	reg ctrlflush_M;
	always @(posedge clk) if(lock) begin
		{PC4_M,	aluout_M,savepc_M,wregno_M,wrreg_M,wmem_M, regout2_M,rmem_M} <= (!init) ? {PC4_A,aluout_A,savepc_A,wregno_A,wrreg_A,wmem_A,regout2_A,rmem_A}:
																						  {32'hXXXXXXXX, 32'hXXXXXXXX, 1'b0, 4'hX,1'b0, 1'b0, 4'hX, 1'b0};
		ctrlflush_M <= (!init) ? ctrlflush_A : 1'b0;
		PCcorr_M <= (!init) ? PCcorr_A : 32'b0;
	end

	wire [(DBITS-1):0] memout_M; // Memory output generated in M, used in W
	wire [(DBITS-1):0] nonmem_M=savepc_M?PC4_M:aluout_M; // For non-memory results, choose between ALU result and PC+4

	//registers from M to W
	//generating all the wires
	reg [(DBITS-1):0] memout_W, nonmem_W;
	reg [3:0] wregno_W;
	wire [(DBITS-1):0] regval_M = rmem_M?dbus:nonmem_M;
	reg wrreg_W, rmem_W;
	always @(posedge clk) if(lock) begin
		{memout_W,nonmem_W,wregno_W,wrreg_W,rmem_W} <= (!init) ? {dbus, nonmem_M, wregno_M, wrreg_M,rmem_M}: 
																 {32'hXXXXXXXX, 32'hXXXXXXXX, 4'hX, 1'b0,1'b0};
	end

	//W Stage
	wire [(DBITS-1):0] regval_W = rmem_W?memout_W:nonmem_W;
endmodule
